\documentclass[a4paper,10pt]{article}
\usepackage[spanish]{babel} %Paquetes de idioma
\usepackage[latin1]{inputenc} %Paquetes de idioma
\usepackage{graphicx} % Paquete para ingresar gráficos
\usepackage{hyperref}
\usepackage{fancybox}

%Encabezado y Pié de página
\input{EncabezadoyPie.tex}
%Carátula del Trabajo
\title{ \input{Portada.tex} }

\begin{document}
	\maketitle %Hace que el título anterior sea el principal del documento
	\newpage

	\tableofcontents %Esta línea genera un indice a partir de las secciones y subsecciones creadas en el documento
	\newpage

	\section{Resumen}
		El objetivo del presente trabajo consiste en realizar una aplicaci\'on en lenguaje C, la cual debe implementar, ademas, una serie de funciones en \emph{assembly}, usando como herramientas el set de instrucciones basado en la arquitectura MIPS y el concepto de ABI exhibido por la c\'atedra.
		\vspace{1cm}
 	\section{Introducci\'on Te\'orica}
		\label{sec:InfoTeo}
		El trabajo a desarrollar consta de una aplicaci\'on que le permita al usuario obtener un volcado octal a partir de una entrada binaria y su operaci\'on inversa, un volcado binario a partir de una entrada octal. Siendo requerimiento necesario que el c\'odigo correspondiente a la l\'ogica que genera los volcados sean codificadas en lenguaje ensamblador, basado en la arquitectura \emph{MIPS32}, respetando la convenci\'on dada por la ABI antes mencionada. \\
		\indent Una vez desarrollados el programa en lenguaje C y las funciones en ensamblador, se debi\'o recrear las condiciones dadas por un procesador MIPS32. Para lo, cual se utiliz\'o un emulador denominado GXemul, que nos permite recrear diferentes arquitecturas, entre las cuales se encuentra MIPS.\\
		\indent A continuaci\'on se provee un breve resumen sobre cada uno de estos conceptos.
		
		\subsection{MIPS (\emph{Microprocessor without Interlocked Pipeline Stages})}
			Se conoce con el nombre MIPS\cite{proc} a una familia de microprocesadores que implementan la arquitectura RISC (\emph{reduced instruction set computer})
			desarrollados por MIPS Technologies\cite{tech}.\\
			\indent Este tipo de procesador se caracteriza por tener un conjunto de instrucciones simples para acceso a memoria \emph{Load/Store}. Presenta 32 registros 
			de 32 bits para el manejo de n\'umeros enteros (\$0 - \$31) y 32 registros de 32 bits para el manejo de n\'umeros en punto flotante.\\
			\indent Los registros para el manejo de n\'umeros enteros son como se muestra en la siguiente tabla:\\[1cm]
			\begin{center}
			\begin{tabular}{|c|c|c|}
				\hline
				\textbf{\normalsize Nombre} & \textbf{\normalsize N\'umero} & \textbf{\normalsize Uso} \\
				\hline
				\normalsize \$zero & \normalsize 0 & \normalsize Constante cero \\
				\hline
				\normalsize \$at & \normalsize 1 & \normalsize  Reservado por el ensamblador\\
				\hline
				\normalsize \$v0 - \$v1 & \normalsize 2 - 3 & \normalsize Evaluaci\'on de expreciones y resultados de una funci\'on \\
				\hline
				\normalsize \$a0 - \$a3 & \normalsize 4 - 7 & \normalsize Argumentos\\
				\hline
				\normalsize \$t0 - \$t7 & \normalsize 8 - 15 & \normalsize Temporales (no necesitan ser preservados)\\
				\hline
				\normalsize \$s0 - \$s7 & \normalsize 16 - 23 & \normalsize Temporales salvados (si necesitan ser preservados)\\
				\hline
				\normalsize \$t8 - \$t9 & \normalsize 24 - 25 & \normalsize Temporales (no necesitan ser preservados)\\
				\hline
				\normalsize \$k0 - \$k1 & \normalsize 26 - 27 & \normalsize Reservados para el Kernell\\
				\hline
				\normalsize \$gp & \normalsize 28 & \normalsize Global Pointer\\
				\hline
				\normalsize \$sp & \normalsize 29 & \normalsize Stack Pointer\\
				\hline
				\normalsize \$fp & \normalsize 30 & \normalsize Frame Pointer\\
				\hline
				\normalsize \$ra & \normalsize 31 & \normalsize Direcci\'on de retorno\\
				\hline
			\end{tabular}
			\end{center}

			\indent Ademas de los anteriormente mencionados, el procesador cuenta con tres registros especiales:
			\begin{itemize}
				\item \textbf{pc}: Program Counter
				\item \textbf{LO}: Registro utilizado para almacenar el resultado en las operaciones de multiplicaci\'on y divisi\'on
				\item \textbf{HI}: Registro utilizado para almacenar el resultado en las operaciones de multiplicaci\'on y divisi\'on
			\end{itemize}
			\indent Todas las instrucciones del set estan formadas por 32 bits, y se pueden clasificar en tres tipos:
			\begin{itemize}
				\item \textbf{Tipo I \emph{Inmediato}}: Presenta 6 bits para indicar el c\'odigo de operaci\'on, 5 bits para indicar el registro fuente, 5 bits
				para indicar el registro destino y 16 bits para indicar un n\'umero inmediato / desplazamiento.
				\item \textbf{Tipo R \emph{Registro}}: Consta de 6 bits para indicar el c\'odigo de operaci\'on, 5 bits para indicar el registro fuente 1, 5 bits
				para indicar el registro fuente 2, 5 bits para indicar el registro destino, 5 bits para indicar desplazamiento y 6 bits para indicar
				el c\'odigo de funci\'on.
				\item \textbf{Tipo J \emph{Jump}}: Esta formada por 6 bits para el c\'odigo de operacion y 26 para indicar la direcci\'on de salto.
			\end{itemize}
			\indent Finalmente, la arquitectura MIPS clasifica a la memoria en 3 sectores:
			\begin{itemize}
				\item \textbf{Segmento de texto}
				\item \textbf{Segmento de datos (\emph{est\'aticos y din\'amicos})}
				\item \textbf{Pila}
			\end{itemize}

		\subsection{ABI (\emph{Application Binary Interface})}
			La ABI es una convenci\'on que describe como debe ser la interface entre aplicaciones, o entre una aplicaci\'on y el sistema operativo. Para 
			el desarrollo del trabajo, se tomo como referencia la ABI dada para el procesador MIPS. Para m\'as informaci\'on sobre la misma se puede
			consultar el documento \emph{System V - Application Binary Interface}\cite{abi}

		\subsection{GXemul}
			Es un emulador gratuito y Open Source que permite correr varias arquitecturas, entre ellas MIPS. Tiene como ventajas que puede correr algunos 
			sistemas operativos sin modificaciones (especialmente \emph{netBSD}), su portabilidad y velocidad de emulaci\'on dado que realiza la 
			traducci\'on binaria en tiempo real. En esta materia utilizaremos el mismo para emular una arquitectura \emph{MIPS 32} y poder obtener 
			el c\'odigo \emph{assembly} correspondiente a programas codificados en C. 
			
	\section{Desarrollo}
		A partir de los conceptos desarrollados en la secci\'on anterior (\ref{sec:InfoTeo}) se procedi\'o a crear la aplicaci\'on, a continuaci\'on se detalla su funcionamiento y ejecuci\'on:
		\vspace{0.5cm}
		\subsection{Modo de Operaci\'on}
			La aplicaci\'on desarrollada debe recibir una entrada (que puede ser por la entrada standard o por un archivo) y genera una salida (que puede ser a un archivo o a la salida standard). La aplicaci\'on puede ejecutarse en 2 modos, el primero es en modo octal (opci\'on default) en el cual genera un volcado octal de la entrada. El segundo modo es en modo binario el cual recibe una entrada que es un volcado octal valido y genera el archivo binario correspondiente. A continuaci\'on se especifica los comandos que se pueden o deben ingresar para correr el programa:
			\begin{itemize}
				\item \textbf{-h --help:} Muestra el mensaje de ayuda.
				\item \textbf{-V --version:} Muestra la versi\'on del programa.
				\item \textbf{-O --octal:} Modo volcado octal (default). Codifica la entrada binaria en un formato octal.
				\item \textbf{-b --binary:} Modo volcado binario. Codifica la entrada octal en un formato binario.	
				\item \textbf{-i --input:} Ruta al archivo de entrada (default \emph{stdin})
				\item \textbf{-o --output:} Ruta al archivo de salida (default \emph{stdout})							
				\item \textbf{-l --length:} Numero de bytes octales por linea (default 0, sin limitaci\'on).
				\item \textbf{-d --delimiter:} Delimitador entre bytes octales. Puede ser un caracter o un string (default: none)
			\end{itemize}
			
		\vspace{0.5cm}
		\subsection{Ejemplos}
		     \begin{verbatim}

			$ echo -n "prueba de volcado octal" | ./tp0 -l 3 -d " "
			160 162 165
			145 142 141
			040 144 145
			040 166 157
			154 143 141
			144 157 040
			157 143 164
			141 154 

			$ echo -n "ida y vuelta" | ./tp0 -O -l 2 -d "@" | ./tp0 -b -l 2 -d "@"
			ida y vuelta
		     \end{verbatim}
	\section{Implementaci\'on}
	Algunas decisiones que se tomaron en la implementaci\'on fueron:
	\begin{itemize}
		\item Siempre que se ingrese el parametro de ayuda(-h o --help) o el de version (-V o --version) se ignoraran los demas parametros y se mostrara la ayuda o la version respectivamente y terminara la ejecuci\'on del programa. Por ejemplo:
		\begin{verbatim}
			$ echo -n "test" | ./tp0 -O -l 2 -d "@" -h
		\end{verbatim}
		imprimira e mensaje de ayuda solamente.
		\item Si se ingresa un comando repetido solo se tendr\'a en cuenta al \'ultimo.
		\item Si el valor del parametro de longitud (-l o --length) no es un numero entero positivo se ignorara y se usara el valor default de este.
		\item En el modo binario, el delimitador de bytes octales indicado por parametro no debe ser necesariamente el mismo con el que se ha generado el volcado octal, solo debe tener la misma longitud, ya que solo se trata de un delimitador que no es parte de la salida. Por ejemplo la siguiente ejecuci\'on es
		\begin{verbatim}
		$ echo -n "distinto" | ./tp0 -l 2 -d "#" | ./tp0 -b -l 2 -d "@"
		distinto
		\end{verbatim}
	\end{itemize}

	\indent Para desarrollar la apilcaci\'on en assembly se modulariz\'o esta en peque\~nas funciones para que sea mucho mas sencillo llevar el desarrollo a cabo y saber que porci\'on de c\'odigo es la que falla. \\
	\indent A partir de las siguientes funciones se desarrollaron las funciones \emph{odump\_encode} y \emph{odump\_decode}
	\begin{itemize}
		\item \emph{read\_asm}: Encapsula el llamado al systemcall read.
		\item \emph{write\_asm}: Encapsula el llamado al systemcall write.
		\item \emph{strlen\_asm}: Calcula el largo de una cadena de caracteres terminada en \textbackslash 0
		\item \emph{bin2oc}: Genera un byte octal a partir del un byte binario.
		\item \emph{oc2bin}: Genera un byte binario a partir de un byte octal.
	\end{itemize}
	
	\indent Todas las funciones manejan poseen manejo de errores. Este manejo se realiza a traves del vector \emph{odump\_errmsg}, cada funcion retorna un c\'odigo de error, a partir de este c\'odigo se obtiene el mensaje de error indexando este vector con el c\'odigo retornado. Los errores que se definieron con los codigos son los siguientes:\\[1cm]
			\begin{center}
			\begin{tabular}{|c|c|}
				\hline
				\textbf{\normalsize C\'odigo} & \textbf{\normalsize Mensaje} \\
				\hline
				\normalsize 0 & \normalsize OK \\
				\hline
				\normalsize 1 & \normalsize No se pudo leer el archivo \\
				\hline
				\normalsize 2 & \normalsize No se pudo escribir en el archivo \\
				\hline
				\normalsize 3 & \normalsize Error al intentar abrir el archivo \\
				\hline
				\normalsize 4 & \normalsize Error en el archivo octal, byte mayor a 255 \\
				\hline

			\end{tabular}
			\end{center}
	
\section{Compilaci\'on y Ejecuci\'on}
	\subsection{Compilaci\'on}
		Para compilar el programa se provee en el CD un archivo Makefile para compilarlo. Para esto desde una terminal en el directorio donde se encuetra el archivo Makefile se debe ejecutar el comando:
	\begin{verbatim}
		$ make
	\end{verbatim}
	Esto generara el archivo ejecutable tp0.
	\subsection{Ejecuci\'on}
		Para ejecutar el programa, una vez compilado, desde la misma terminal se ejecuta el archivo tp0 con los parametros deseados, un ejemplo de ejecuci\'on es:
	\begin{verbatim}
		$ ./tp0 -i /dev/urandom -l 12 -d " "
	\end{verbatim}
\section{Pruebas}
	A continuaci\'on se muestran algunas capturas de pantalla para distintas ejecuciones del programa.
	\begin{itemize}
		\item Imprime la version por pantalla.
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/version.png}
			\end{center}
			
		\item Imprime la ayuda por pantalla.
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/help.png}
			\end{center}
			
		\item Ejecuci\'on de volcado octal con input por stdin y output por stdout
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/prueba1.png}
			\end{center}
		\item Ejecuci\'on de volcado octal con input por stdin y output por stdout, con 4 bytes octales por linea y delimitador @
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/prueba2.png}
			\end{center}
		\item Ejecuci\'on en cadena de volcado octal con input por stdin y tomando el output por stdout de esta como input para convertirlo a binario nuevamente.
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/prueba3.png}
			\end{center}
		\item Se crea un archivo (/tmp/test.bin) y se hace un volcado octal de este a el archivo /tmp/test.oc, luego se muestra la salida utilizando el comando cat y a continuci\'on se hace un vocado binario de este obteniendo el archivo original.
			\begin{center}
				\includegraphics[width=0.8\textwidth]{img/prueba4.png}
			\end{center}
	\end{itemize}
	\subsection{Pruebas Automatizadas}
		En el CD adjunto al Trabajo Pr\'actico donde se entregan los fuentes del ejecutable se proporciona un script que ejecuta test automaticos del programa comparando la salida con la salida del comando \emph{od} utilizando el comando \emph{diff}. \\
		El script genera \emph{n} archivos de \emph{b} bytes a partir de /dev/urandom y genera un volcado octal con el comando od y con el programa hecho por nosotros, luego compara ambas salidas con el comando diff. Una vez hecho esto convierte a binario los volcados octales y los compara nuevamente usando diff con los archivos originales.\\
		El script tiene 2 parametros:
		\begin{itemize}
			\item \textbf{-b:} Indica la cantidad de bytes que se usan del archivo /dev/urandom.
			\item \textbf{-n:} Indica la cantidad de loops de ejecuci\'on del script.
		\end{itemize} 
		Un ejemplo de ejecuci\'on del script es:
		\begin{verbatim}
			$ ./test_tp1_6620.sh -b 1024 -n 50
		\end{verbatim}
		
	\section{Conclusi\'on}
	En el presente trabajo se resolvi\'o un problema piloto planteado en el trabajo pr\'actico anterior pero esta vez implementando la l\'ogica en assembly de MIPS. En este caso desarrollamos la aplicacio\'on respetando la ABI que es la convencion utilizada por los desarrolladores (y por gcc) para que los programas puedan hacer llamadas a funciones de librerias y no tener ningun problema.
	\indent Tambien pudimosentender un poco mas el funcionamiento de las llamadas al sistema, en este caso se utilizaron dos llamadas, una para leer (\emph{SYS\_read}) y otra para escribir (\emph{SYS\_write}) desde y hacia archivos respectivamente.
	\newpage
	\section{Ap\'endice}
	\subsection{C\'odigo Fuente}

	\subsubsection{main.c}
		\begin{verbatim}
		/*
 ============================================================================
 Name        : main.c
 Author      : Agustin Pivetta, Diego Rivas
 Version     : 1.0
 Description : Tp1 66.20: Dump Octal en MIPS
 ============================================================================
 */

#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include "odump.h"

//Extras hasta que se complete el desarrollo en assembly @BORRAR
#include <unistd.h>

#define MIN_NUM_ASCII 48
#define MAX_NUM_ASCII 57

/*
 * Imprimo la ayuda
 */
void printHelp() {
	printf(
			"Usage:\n"
					"  tp1 -h\n"
					"  tp1 -V\n"
					"  tp1 [options]\n"
					"Options:\n"
					"  -h, --help      Print this information.\n"
					"  -V, --version   Print version and quit.\n"
					"  -O, --octal     Octal dump mode: encode the input (binary file) in \n"
					"                  octal format (default).\n"
					"  -b, --binary    Binary mode: decode the input (octal dump file) in\n"
					"                  binary format.\n"
					"  -i, --input     Path to input file (defaults to stdin).\n"
					"  -o, --output    Path to output file (defaults to stdout).\n"
					"  -l, --length    Number of octal bytes per line (line length) at the\n"
					"                  output in octal dump mode (0 = default = no limit).\n"
					"  -d, --delimiter Delimiter between octal bytes. Could be a string, not\n"
					"                  only a single byte. Used as delimiter for octal output\n"
					"                  and input files (default: none).\n");

}

/*
 * Imprimo la version
 */
void printVersion() {
	printf("66.20 - TP1: Octal Dump, Versión 1.00\n");
	return;
}
/*
 * Parseo number y si es un numero entero valido lo casteo a entero
 */
size_t parseInt(const char* number) {
	char len = strlen(number);

	// Si me envían un cero devuelvo 0
	if ((len == 1) && (number[0] == '0'))
		return 0;

	size_t i = 0;
	// Recorro a m y verifico que cada uno de sus caracteres sean números
	for (i = 0; i < len; i++) {
		if ((number[i] < MIN_NUM_ASCII) || (number[i] > MAX_NUM_ASCII))
			return 0;
	}

	// Si m es una cadena válida la convierto en un número
	unsigned int aux = 0;
	sscanf(number, "%u", &aux);
	return aux;
}

/*
 * Abro los archivos de input/outout
 */
FILE* openFile(char* fileName, FILE* default_file, const char* mode) {
	FILE* fd;
	if (fileName == NULL ) {
		fd = default_file;
	} else {
		fd = fopen(fileName, mode);
	}

	if (!fd) {
		fprintf(stderr, odump_errmsg[3], fileName);
		exit(-1);
	}
	return fd;
}

int main(int argc, char** argv) {
	// Fuerzo que un error en getopt no se imprima por stderr
	opterr = 0;
	// Indica cuando terminé de procesar todos los argumentos
	int nextOpt = 0;
	// Cadena que lista las opciones cortas válidas
	const char* const opCortas = "hVObi:o:l:d:";
	// Estructura de structs describiendo los valores largos
	const struct option opLargas[] = { { "help", no_argument, NULL, 'h' }, {
			"version", no_argument, NULL, 'V' }, { "octal", no_argument, NULL,
			'O' }, { "binary", no_argument, NULL, 'b' }, { "input",
			optional_argument, NULL, 'i' }, { "output", optional_argument, NULL,
			'o' }, { "length", optional_argument, NULL, 'l' }, { "delimiter",
			optional_argument, NULL, 'd' }, { NULL, no_argument, NULL, 0 } };

	// Variable donde recibo el nombre del archivo de entrada
	char* inputFName = NULL;
	// Variable donde recibo el nombre del archivo de salida
	char* outputFName = NULL;
	// Variable donde guardo el modo de ejecucion (1:octal dump mode | 2: binary dump mode)
	char mode = 1;
	// Variable donde guardo la longitud de numeros octal por linea
	size_t length = 0;
	// Variable donde guardo el delimitador de bytes octales
	char* delimiter = NULL;
	//Retorno de las funciones de encode y decode
	int rc = 0;

	while (1) {
		//Leo que me devuelve get_opt
		nextOpt = getopt_long(argc, argv, opCortas, opLargas, NULL );

		if (nextOpt == -1) {
			break;
		}

		switch (nextOpt) {
		case 'h': {
			printHelp();
			return 0;
			break;
		}

		case 'V': {
			printVersion();
			return 0;
			break;
		}

		case 'O': {
			mode = 1;
			break;
		}

		case 'b': {
			mode = 2;
			break;
		}

		case 'i': {
			if (strcmp(optarg, "-") != 0) {
				inputFName = malloc(strlen(optarg) + 1);
				strcpy(inputFName, optarg);
			}
			break;
		}

		case 'o': {
			if (strcmp(optarg, "-") != 0) {
				outputFName = malloc(strlen(optarg) + 1);
				strcpy(outputFName, optarg);
			}
			break;
		}

		case 'l': {
			length = parseInt(optarg);
			break;
		}

		case 'd': {
			delimiter = malloc(strlen(optarg) + 1);
			strcpy(delimiter, optarg);
			break;
		}

		default: {
			printHelp();
			exit(-1);
			break;
		}
		}
	}

	FILE* input = openFile(inputFName, stdin, "rb");
	FILE* output = openFile(outputFName, stdout, "w");

	//Obtengo los file descriptors
	int infd = fileno(input);
	int outfd = fileno(output);

	//Seteo las opciones
	struct odump_opts opts = { length, delimiter };

	if (mode == 1) {
		rc = odump_encode(infd, outfd, &opts);
	} else if (mode == 2) {
		rc = odump_decode(infd, outfd, &opts);
	}

	fclose(input);
	fclose(output);

	if (rc != 0) {
		fprintf(stderr, "%s", odump_errmsg[rc]);
        return rc;
	}

	return 0;
}

		\end{verbatim}
	\subsubsection{odump.h}
		\begin{verbatim}
		/*
 ============================================================================
 Name        : odump.h
 Author      : Agustin Pivetta, Diego Rivas
 Version     : 1.0
 Description : Tp1 66.20: funciones y estructuras de datos auxiliares para 
	       realizar los cómputos de encoding y decoding
 ============================================================================
*/ 
#include <stdio.h>
#include <stdlib.h>

//Estructura de opciones
struct odump_opts {
	size_t length;
	const char* delim;
};

//Vector de mensages de error
extern const char* odump_errmsg[];

//Prototipos de funciones
int odump_encode(int infd, int outfd, struct odump_opts *opts);
int odump_decode(int infd, int outfd, struct odump_opts *opts);
void bin2oc(unsigned char bin, char* octal);
unsigned char oc2bin(char octal[]);
size_t write_asm(int fildes, const void *buf, size_t nbytes);
ssize_t read_asm(int fildes, void *buf, size_t nbyte);
size_t strlen_asm(const char *str);

		\end{verbatim}
	\subsubsection{odump.S}
		\begin{verbatim}
		//######################################################################
//const char *odump_errmsg[];
        .rdata
        .align  2
$msg0:
        .asciiz  "OK"
$msg1:
        .asciiz  "No se pudo leer el archivo\n"
$msg2:
        .asciiz  "No se pudo escribir en el archivo\n"
$msg3:
        .asciiz  "Error al intentar abrir el archivo %s\n"
$msg4:
        .asciiz  "Error en el archivo octal, byte mayor a 255\n"

        .globl  odump_errmsg
        .data
        .type   odump_errmsg, @object
        .size   odump_errmsg, 20
odump_errmsg:
        .word   $msg0
        .word   $msg1
        .word   $msg2
        .word   $msg3
	.word   $msg4

//######################################################################
//void bin2oc(unsigned char bin, char* octal) {

// Diagrama de Stack
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

#include <mips/regdef.h>
#include <sys/syscall.h>

.data
    end:  .ascii  "\0"

.text
.align 2


.globl bin2oc
.ent bin2oc

bin2oc:
	.frame $fp,24,ra
        .set noreorder
        .cpload t9
        .set reorder

	// creo stack frame
        subu sp,sp,24           #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 0

        sw $fp,20(sp)           #Respaldo la posicion del frame pointer
        sw gp,16(sp)            #Respaldo la posicion del global pointer
        move $fp,sp

        sw a0,24(sp)            #bin
        sw a1,28(sp)            #octal

        li t0,2                 #i = 2, contador del loop
        li t1,8                 #divisor = 8, constante
        move t2,a0              #t2 = bin = dividendo

$FOR:
        div t2,t1               #LO = t2/t1 y HI = t2 mod t1
        mflo t2                 #Dividendo = Cociente
        mfhi t3
        addi t3,t3,48           #resto + 48 forma el ascii del numero
        addu t4,a1,t0           #puntero a vector "octal" + offset
        sb t3,0(t4)             #octal[i] = ascii
        addi t0,t0,-1           #Resto 1 al contador
        bgez t0,$FOR

        lw $fp,20(sp)           #Recupero la posicion del frame pointer
        lw gp,16(sp)            #Recupero la posicion del global pointer
        addu sp,sp,24
        j ra
.end bin2oc

//######################################################################
//unsigned char oc2bin(char octal[]) {

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl oc2bin
.ent oc2bin

oc2bin:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento en el ABA del caller
    
    	lbu t0,0(a0)    # cargo el primer char del octal[3]
    	subu t0,t0,48
        bgt t0,7,oc2bin_error
        blt t0,0,oc2bin_error
    	sll t0,t0,6     # t0 = t0*64 = t0*(2^6) 
        
    	lbu t1,1(a0)    # cargo el segundo char del octal[3]
    	subu t1,t1,48
        bgt t1,7,oc2bin_error
        blt t1,0,oc2bin_error
    	sll t1,t1,3     # t0 = t0*8 = t0*(2^3) 
        
    	lbu t2,2(a0)    # cargo el tercer char del octal[3]
    	subu t2,t2,48
        bgt t2,7,oc2bin_error
        blt t2,0,oc2bin_error
    	sll t2,t2,0     # t0 = t0*1 = t0*(2^0) 
        
    	addu v0,t0,t1
    	addu v0,v0,t2

        blt v0,256,oc2bin_ok	#Verifica que los bytes sean menores que 256  
        
oc2bin_error:
	//hubo error en el byte
	li v1,4			#Cargo el codigo de error
	li v0,0			#Vacio el resultado

oc2bin_ok:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end oc2bin

//######################################################################
//Esta funcion es la equivalente a write() de C
//size_t write_asm(int fildes, const void *buf, size_t nbytes);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl write_asm
.ent write_asm

write_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_write # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,write_error	#Ocurrio un error en la escritura
	beq a3,zero,write_exit
    
write_error:

	move v0,zero
	addi v1,zero,2	#Pongo a v1 en 2 para indicar error de escritura

write_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end write_asm

//######################################################################
//Esta funcion es la equivalente a read() de C
//ssize_t read_asm(int fildes, void *buf, size_t nbyte);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl read_asm
.ent read_asm

read_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_read # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,read_error	#Ocurrio un error en la lectura
	beq a3,zero,read_exit
    
read_error:

	move v0,zero
	addi v1,zero,1	#Pongo a v1 en 1 para indicar error en la lectura	

read_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end read_asm

//######################################################################
//Esta funcion es la equivalente a strlen() de C
//size_t strlen_asm(const char *str);

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl strlen_asm
.ent strlen_asm

strlen_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
    .cprestore 16
    
    sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
    sw a0,24($fp)	#guardo el argumento 0 en el ABA del caller
    
	move v0,zero    #inicializo en cero el retorno
	lb t1,end	#Cargo el "\0"

$LOOP:
	add t2,a0,v0	#inicio de vector + len
	lb t0,0(t2)	#Leo una letra
	addi v0,v0,1	#incrementa la longitud
	bne t0,t1,$LOOP

	addi v0,v0,-1	#Hay que restar 1 al resultado porque no se cuenta al \0 como letra

	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end strlen_asm


//######################################################################
//int odump_encode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 48 - [  a2  ]
// 44 - [  a1  ]
// 40 - [__a0__]_____________________
// 36 - [  pad ]
// 32 - [  ra  ] Saved Register Area
// 28 - [  fp  ]
// 24 - [  gp  ]_____________________
// 20 - [      ] Local Temp Area
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_encode
.ent odump_encode

odump_encode:
    .frame $fp,40,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,40   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 24
    
    sw ra,32(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,28(sp)   #Respaldo la posicion del frame pointer
	sw gp,24(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,40($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,44($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,48($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
    //leo de infd por primera vez
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

$WHILE_ENCODE:
    //paso el binario a octal
    lbu a0,bin_encode(zero)
    la a1,oc_encode
    jal bin2oc
    
    //escribo en outfd
    lw a0,44($fp)
    la a1,oc_encode
    li a2,3
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,3
    bne t0,v0,$OUT_ENCODE # si no escribio 3 caracteres salgo
    
    // chequeo si tengo que escribir un salto de linea
    lw t0, 16($fp)  # levanto el contador de bytes octales
    addiu t0,t0,1   # le sumo 1
    sw t0, 16($fp)  # guardo el contador de bytes octales
    lw t1,48($fp)   # levanto el puntero al struct
    lw t2,0(t1)  # leo el valor de length
    bne t0,t2,$DELIM_ENCODE
    
    //escribo un salto de linea
    //antes leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    lw a0,44($fp)
    la a1,endline_encode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no escribio 1 caracter salgo
    
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    b $WHILE_ENCODE
    
$DELIM_ENCODE:
    //leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    //escribo el delimitador
    lw t0,48($fp)               # levanto el puntero al struct
    lw a0,4(t0)                 # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_ENCODE       # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm              # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)               # guardo el largo del delimitador
    beqz v0,$WHILE_ENCODE
    lw a0,44($fp)
    lw t0,48($fp)               # levanto el puntero al struct
    lw a1,4(t0)                 # cargo el puntero al delimitador en a1
    addiu a2,v0,0               # cargo el valor devuelto por strlen_asm en a2
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    lw t0,20($fp)               # levanto el largo del delimitador
    bne t0,v0,$OUT_ENCODE       # si no escribio el delimitador salgo
    b $WHILE_ENCODE

$ERR_ENCODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_ENCODE

$OUT_ENCODE:
        lw ra,32(sp)	#Recupero la posicion del return address
	lw $fp,28(sp)	#Recupero la posicion del frame pointer
	lw gp,24(sp)	#Recupero la posicion del global pointer
	addu sp,sp,40
	j ra
.end odump_encode

    
    
//######################################################################
//int odump_decode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 56 - [  a2  ]
// 52 - [  a1  ]
// 48 - [__a0__]_____________________
// 44 - [  pad ]
// 40 - [  ra  ] Saved Register Area
// 36 - [  fp  ]
// 32 - [  gp  ]_____________________
// 28 - [      ] 
// 24 - [      ] Local Temp Area
// 20 - [      ] 
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_decode
.ent odump_decode

odump_decode:
    .frame $fp,48,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,48   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 32
    
    sw ra,40(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,36(sp)   #Respaldo la posicion del frame pointer
	sw gp,32(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,48($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,52($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,56($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0         #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
$WHILE_DECODE:
la t0,oc_decode
sb zero,0(t0)
sb zero,1(t0)
sb zero,2(t0)


//leo de infd
	lw a0,48($fp)
    la a1,oc_decode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no leyo 1 caracteres salgo
    
    lb t0,oc_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    //leo los 2 caracteres que me faltan
	lw a0,48($fp)
    la t0,oc_decode
    addi a1,t0,1    # leo a partir de oc_decode[1]
    li a2,2
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,2
    bne t0,v0,$OUT_DECODE # si no leyo 2 caracteres sigo

$SIGO:
    //paso el octal a binario
    la a0,oc_decode
    jal oc2bin
    bne v1,zero,$ERR_DECODE	#Verifica errores
    sw v0,bin_decode
    
    //escribo en outfd
    lw a0,52($fp)
    la a1,bin_decode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no escribio 1 caracter salgo
    
$DELIM_DECODE:
    //leo el delimitador
    lw t0,56($fp)           # levanto el puntero al struct
    lw a0,4(t0)             # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_DECODE   # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm          # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)           # guardo el largo del delimitador
    beqz v0,$WHILE_DECODE   # si el largo es 0 paso
    
    li t0,0
    sw t0,24($fp)
$READ_DELIM:
    lw a0,48($fp)
    la a1,void_decode       # leo en void
    li a2,1                 # leo 1 byte
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE   # si no leyo 1 byte salgo
    
    lw t0,void_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    lw t0,24($fp)           # levanto el contador
    addiu t0,t0,1           # incremento el contador
    sw t0,24($fp)           # guardo el contador incrementado
    lw t1,20($fp)           # levanto el largo del delimitador
    bgt t1,t0,$READ_DELIM
    li t0,0                 # reinicio a 0 el contador
    sw t0,24($fp)           # guardo el contador
    b $WHILE_DECODE

$ERR_DECODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_DECODE

$OUT_DECODE:
    lw ra,40(sp)	#Recupero la posicion del return address
	lw $fp,36(sp)	#Recupero la posicion del frame pointer
	lw gp,32(sp)	#Recupero la posicion del global pointer
	addu sp,sp,48
	j ra
.end odump_decode

.data

    endline_encode: .ascii "\n"
    bin_encode: .byte 0
    oc_encode: .byte 0,0,0,0

    endline_decode: .ascii "\n"
    bin_decode: .byte 0
    oc_decode: .byte 0,0,0
    void_decode: .byte 0

		\end{verbatim}
	\subsubsection{Makefile}
		\begin{verbatim}
		CFLAGS=-g -Wall -lm -std=c99
EXEC= tp1
CC=gcc

all: $(EXEC)
	
$(EXEC): main.c
	$(CC) $(CFLAGS) *.c *.S -o $(EXEC)
		\end{verbatim}
	% Citas bibliográficas.
	\begin{thebibliography}{99}
		\bibitem{proc} Informaci\'on adicional sobre MIPS, http://es.wikipedia.org/wiki/MIPS\_(procesador).
		\bibitem{tech} MIPS Technologies, http://www.mips.com/.
		\bibitem{abi} MIPS ABI, http://refspecs.freestandards.org/elf/mipsabi.pdf/.
	\end{thebibliography}
\end{document}


